﻿using IrcDotNet;
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Serialization;

namespace StreamersBestFriend
{
	class exIrcClient
	{

		public IrcClient _ircclient;
		private VlcMediaPlayer _vlcplayer;
		private BotSettings _settings;
		private Blacklist _blacklist;
		private MainWindow _ui;

		int votecounter;
		int maxvotecounter = 10;

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="pSettings">the settings object</param>
        /// <param name="pUi">the ui object</param>
        /// <param name="pVlc">the vlcplayer object</param>
		public exIrcClient(BotSettings pSettings, MainWindow pUi, VlcMediaPlayer pVlc)
		{
			this._ui = pUi;
			_settings = pSettings;
			_vlcplayer = pVlc;
			initializeIRC();
			_blacklist = new Blacklist(_ircclient.Users);
		}

        /// <summary>
        /// destructor
        /// </summary>
		 ~exIrcClient()
		{
            /*
			XmlSerializer s = new XmlSerializer(typeof(BotSettings));
			TextWriter writer = new StreamWriter(@"C:\IrcBotSettings.xml");
			s.Serialize(writer, this._settings);
			writer.Close();
              * */
		}

        /// <summary>
        /// initializes the irc module, sets events, calls the connect function, etc.
        /// </summary>
		public void initializeIRC()
		{
			IrcUserRegistrationInfo _ircuserinfo = new IrcUserRegistrationInfo()
			{
				NickName = _settings.IRC_Username,
				UserName = _settings.IRC_Username,
				RealName = _settings.IRC_Username,
				Password = _settings.IRC_Password
			};

			//create irc client class
			_ircclient = new IrcClient();
			_ircclient.FloodPreventer = new IrcStandardFloodPreventer(4, 2000);
			_ircclient.Connected += IrcClient_Connected;
			_ircclient.ConnectFailed += IrcClient_ConnectFailed;
			_ircclient.Disconnected += IrcClient_Disconnected;
            _ircclient.PongReceived += _ircclient_PongReceived;
            _ircclient.PingReceived += _ircclient_PingReceived;
            _ircclient.Error += _ircclient_Error;
            
			//_ircclient.Registered += IrcClient_Registered;

			_ircclient.ErrorMessageReceived += IrcClient_ErrorMessageRecieved;

			_ircclient.Connect(_settings.IRC_Serveradress, _settings.IRC_Serverport, false, _ircuserinfo);
			_ui.writeConsole("IRC initialized");
		}



        /// <summary>
        /// fires when an error occurs
        /// </summary>
        void _ircclient_Error(object sender, IrcErrorEventArgs e)
        {
            if(_settings.DebugMode)_ui.writeConsole("Error: " +e.Error.Message);
        }

        /// <summary>
        /// fires when the irc client successfully connected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
		private void IrcClient_Connected(object sender, EventArgs e)
		{
			_ui.writeConsole("Now connected to " + _settings.IRC_Serveradress + " with " + _settings.IRC_Username);
			Thread.Sleep(10000);
			_ui.writeConsole("Attempting to join: " + _settings.IRC_TargetChannel);
			_ircclient.Channels.Join(_settings.IRC_TargetChannel);
			Thread.Sleep(5000);
            if (_ircclient.Channels.Count > 0)
            {
                _ui.writeConsole("Joined channel: " + _settings.IRC_TargetChannel);
                //if (!_settings.DebugMode) sendMessageToChannel("Dont fear, musicbot is here");
                _ircclient.Channels[0].MessageReceived += OnChannelMessageReceived;
            }
		}

        /// <summary>
        /// Debug: fires when pong recieved
        /// </summary>
        void _ircclient_PongReceived(object sender, IrcPingOrPongReceivedEventArgs e)
        {
            if (_settings.DebugMode) _ui.writeConsole("Pong recieved");
        }

        /// <summary>
        /// Debug: fires when ping recieved
        /// </summary>
        void _ircclient_PingReceived(object sender, IrcPingOrPongReceivedEventArgs e)
        {
            if (_settings.DebugMode) _ui.writeConsole("Ping recieved");
        }

        /// <summary>
        /// fires when a channel message is recieved
        /// </summary>
		private void OnChannelMessageReceived(object sender, IrcMessageEventArgs e)
		{
			checkForCommand(e.Source, e.Text);
		}

        /// <summary>
        /// fires when the connection failed
        /// </summary>
		private void IrcClient_ConnectFailed(object sender, IrcErrorEventArgs e)
		{
			_ui.writeConsole("Connection failed: " + e.Error.ToString());
            Thread.Sleep(20000);
            initializeIRC();

		}

        /// <summary>
        /// fires when the irc client disconnects from the server
        /// </summary>
		public void IrcClient_Disconnected(object sender, EventArgs e)
		{
			_ui.writeConsole("Disconnected");
            Thread.Sleep(20000);
			initializeIRC();
		}

        /// <summary>
        /// fires when the client recieves an error message from the server
        /// </summary>
		private void IrcClient_ErrorMessageRecieved(object sender, IrcErrorMessageEventArgs e)
		{
			_ui.writeConsole("ErrorMessageRecieved: " + e.Message);
		}

        /// <summary>
        /// checks the incoming message for a specific command, also checks if the user is blacklisted or not
        /// </summary>
        /// <param name="source">the user</param>
        /// <param name="inputString">the full message</param>
		private void checkForCommand(IIrcMessageSource source, String inputString)
		{
			if (_blacklist.checkUser(source.Name))
			{
				Debug.WriteLine(source.Name + ": Request blocked");
				return;
			}
			String[] s = inputString.Split(' ');

			if (Regex.IsMatch(inputString, @"!songrequest"))
			{
				Debug.WriteLine(source.Name + " " + inputString);
				_vlcplayer.requestSong(inputString);
				return;
			}

			if (Regex.IsMatch(inputString, @"!skip") && checkForOperator(source))
			{
				_vlcplayer.skipSong(true);
				return;
			}

			if (Regex.IsMatch(inputString, @"!voteskip"))
			{
				votecounter++;
				if (votecounter >= maxvotecounter)
				{
					_vlcplayer.skipSong(true);
					sendMessageToChannel("Voteskip successful");
				}
				return;
			}

			if (Regex.IsMatch(inputString, @"!banuser") && checkForOperator(source))
			{
				if (_blacklist.addUser(s[1]))
					sendMessageToChannel("User " + s[1] + " has been blacklisted from requesting");
				return;
			}

			if (Regex.IsMatch(inputString, @"!bansong") && checkForOperator(source))
			{
				_blacklist.addSong(s[1]);
				return;
			}

			if (Regex.IsMatch(inputString, @"!unbanuser") && checkForOperator(source))
			{
				if (_blacklist.removeUser(s[1]))
					sendMessageToChannel("User " + s[1] + " has been blacklisted from requesting");
				return;
			}

			if (Regex.IsMatch(inputString, @"!unbansong") && checkForOperator(source))
			{
				_blacklist.removeSong(s[1]);
				return;
			}

			if (Regex.IsMatch(inputString, @"!checkuser"))
			{
				if (_blacklist.checkUser(s[1]))
				{
					sendMessageToChannel("User " + s[1] + " is blacklisted from requesting."); return;
				}
				sendMessageToChannel("User " + s[1] + " is not blacklisted from requesting.");
				return;
			}

			if (Regex.IsMatch(inputString, @"!checksong"))
			{
				if (_blacklist.checkSong(s[1]))
				{
					sendMessageToChannel("Song " + s[1] + " is blacklisted."); return;
				}
				sendMessageToChannel("Song " + s[1] + " is not blacklisted.");
				return;
			}
		}

        /// <summary>
        /// sends a message to the channel
        /// </summary>
        /// <param name="s">the message</param>
		public void sendMessageToChannel(String s)
		{
            bool silentMode = false;
            _ui.Dispatcher.Invoke(() =>
                {
                    if (_ui._CheckboxSilent.IsChecked == true)
                        silentMode = true;
                    else if (_ui._CheckboxSilent.IsChecked == false)
                        silentMode = false;
                });

			_ui.writeConsole(s);
			if (!_settings.DebugMode && !silentMode) _ircclient.LocalUser.SendMessage(_ircclient.Channels[0].Name, s);
		}

        /// <summary>
        /// checks if the user has the +o flag in the channel
        /// </summary>
        /// <param name="pSource">the user</param>
        /// <returns>true if yes, false if no</returns>
		private bool checkForOperator(IIrcMessageSource pSource)
		{
			foreach (IrcChannelUser iUser in _ircclient.Channels[0].Users)
			{
				if (pSource.Name.Equals(iUser.User.NickName))
				{
					if (iUser.Modes.Contains('o')) return true;
 					return false;
				}
			}
			return false;
		}

        /// <summary>
        /// resets the !voteskip counter
        /// </summary>
		public void resetVoteCounter()
		{
			votecounter = 0;
		}

	}
}
